use std::collections::{BTreeMap, HashMap};
use std::path::PathBuf;

use clap::Parser;
use serde::{Deserialize, Serialize};
use wtask_base::cmd::{file_de_load, file_en_save};
use wtask_base::encrypt::decrypt_str_to_str;
use wtask_base::error::{WError, WResult};
use wtask_base::export::{json, serde_from_str, serde_to_string};
use wtask_base::machine::{machine_port, CMDArgsConfigMachine, CMDArgsConfigAll};
use wtask_app::{AppHome, cli_app, msg_func_none};
use wtask_file::{IncomingBody, Request, Response, StatusCode, header, ssh_install_machine};

use crate::basic::{parse_body_string, response_body, ResponseBody, WEB_SESSION};



#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct LoginStorageData {
    pub(crate) config: CMDArgsConfigMachine,
    pub(crate) home: String,
}

// login data: { token: { machine: SessionData } }, machine ordered
type LoginStorageInner = HashMap<String, BTreeMap<String, LoginStorageData>>;
pub(crate) struct LoginStorage(LoginStorageInner);


impl AsRef<LoginStorageInner> for LoginStorage {
    fn as_ref(&self) -> &LoginStorageInner {
        &self.0
    }
}

impl AsMut<LoginStorageInner> for LoginStorage {
    fn as_mut(&mut self) -> &mut LoginStorageInner {
        &mut self.0
    }
}

impl Default for LoginStorage {
    fn default() -> Self {
        let r = file_de_load("web.login", None, true).unwrap_or_default();
        Self(r)
    }
}

impl LoginStorage {
    pub(crate) fn drop_data(&mut self) {
        let _ = file_en_save("web.login", &self.0, None);
    }
}




pub(crate) async fn response_login(req: Request<IncomingBody>, token: &str) -> WResult<Response<ResponseBody>> {
    let f = req.headers().get("File").is_some();
    let pw = req.headers().get("Passwd").cloned();
    let body = parse_body_string(req, token).await?;
    // log_debug!("Web login data: {body}");
    let configs = if f {
        body.split("\n").filter_map(| v | {
            serde_from_str::<CMDArgsConfigMachine>(v).ok()
        }).collect::<Vec<_>>()
    } else {
        let args_temp = serde_from_str::<Vec<String>>(&body)?;
        let config = match CMDArgsConfigAll::try_parse_from(args_temp) {
            Ok(CMDArgsConfigAll::Machine(mut config)) => {
                config.machine_port = machine_port(config.machine_port, config.proxy);
                config
            },
            Ok(CMDArgsConfigAll::Ssh(config_ssh)) => {
                let passwd = pw.unwrap();
                let passwd = decrypt_str_to_str(passwd.to_str().unwrap(), token)?;
                let password_func = move || passwd;
                ssh_install_machine(config_ssh, password_func)?
            },
            Err(e) => {
                return Err(WError::ParseError(e.to_string()));
            },
            _ => {
                return Err(WError::DataError("Config method not allowed".to_string()));
            }
        };
        vec![config]
    };
    // get home
    let mut errs = Vec::new();
    let mut dir_home = Vec::<PathBuf>::new();
    for config_machine in configs.iter() {
        let config_str = format!("Config: {config_machine:?}");
        match cli_app(AppHome, config_machine, None, msg_func_none).await {
            Ok(Some(dir_home_temp)) => {
                dir_home.push(dir_home_temp.into())
            },
            Err(e) => errs.push(format!("{config_str}, login fail, {e}")),
            _ => errs.push(format!("{config_str}, login fail, no res"))
        };
    }
    let (res_status, res_body) = if errs.is_empty() {
        let mut s = WEB_SESSION.login.write().await;
        if !s.as_ref().contains_key(token) {
            s.as_mut().insert(token.to_string(), Default::default());
        }
        let machine_login = s.as_mut().get_mut(token).unwrap();
        for (config_machine, dir_home_temp) in configs.into_iter().zip(dir_home) {
            machine_login.insert(
                config_machine.machine_token.clone(),
                LoginStorageData {
                    config: config_machine,
                    home: dir_home_temp.to_string_lossy().to_string(),
                }
            );
        }
        let mut data_res = BTreeMap::new();
        for (k, v) in machine_login.iter() {
            data_res.insert(k.to_owned(), (v.config.to_string(), v.home.clone()));
        }
        let json = serde_to_string(&data_res)?;
        (StatusCode::OK, json)
    } else {
        let json = json!({
            "msg": "登录失败",
            "data": errs,
        }).to_string();
        (StatusCode::UNAUTHORIZED, json)
    };

    let response = Response::builder()
        .status(res_status)
        .header(header::CONTENT_TYPE, "application/json")
        .body(response_body(&res_body, token)).unwrap();
    Ok(response)
}

